Typescript 핸드북 다시보기 - 1

Typescript 핸드북 다시보기 - 1

기본 타입

타입 스크립트에는 아래의 기본 타입들이 있다.

  • boolean
  • number
  • string
  • array
  • tuple
  • enum
  • any
  • void
  • null
  • undefined
  • never
  • object

Tuple

튜플은 요소의 쌍을 표현한다. 배열 형태에 타입을 넣어 표현하며 각 요소의 타입은 다를 수 있다.

정해진 index 외에 참조시 오류가 발생한다.

let a: [string, number]
let b: [string, number, any]
...
b[3] // error

Enum

열거형은 값의 집합에 이름을 붙혀 표현한 것 이다.

각 값들은 0으로 시작하며 특정 값으로 매기거나 문자열로 설정할 수 도 있다.

enum Color {Red, Blue, Green} // Red: 0, Blue:1, Green: 2
const color: Color = Color.Green

enum Country {KR = 3, US, UK, ANY = KR | US | UK}
enum Alphabet {A = "A", B = "B", C = "C"}
enum Anser = {No = 0, Yes = "YES"} // 가능하지만 두 타입을 섞는 것은 권장하지 않는다.

역 매핑

enum은 숫자형 열거형에 한하여 역 매핑을 지원한다.

enum Enum {
    A, B
}
const a = Enum.A
const nameOfA = Enum[a] // "A"

const 열거형

const 를 통해 열거형을 선언하고, 상수 열거형 인 경우 컴파일 타임에서 완전히 제거되고 값은 인라인 된다.

const enum Alpha = {
    A = 1,
    // ...
}

console.log(Alpha.A)

/*
컴파일 이후 소스

console.log(1)
*/

Any

뭐든지 올 수 있는 타입에 대해서 any를 붙이며 기존의 JS 처럼 타입 타입 검사를 하지 않아야 할 때 유용하다.

타입을 일부만 알거나 타입이 섞인 경우에도 사용할 수 있다.

const list: any[] = [1, "2", true]

Void

any의 반대로 아무것도 없음을 뜻한다. 기본적으로 undefined나 조건부로 null 만 할당할 수 있다.

Never

항상 오류를 발생시키거나 끝나지 않는 함수를 표현할 때 사용한다.

function error(message: string): never{
    throw new Error(message)
}

function infinite(): never{
    while(true){
    }
}

Type Assertion

컴파일 수준에서 특정 타입이라고 컴파일러에게 알리는 역할을 한다. 실제로 런타임에는 영향이 없다.

// 방법 1
const rawValue: any = "this is message"
const messageCount: number = (<string>rawValue).length

// 방법 2  * TSX 사용 시 이 방법만 사용 가능하다.
const rawValue: any = "this is message"
const messageCount: number = (rawValue as string).length

유니언과 교차 타입

유니언 타입

한 가지의 타입에서 여러 타입을 선택적으로 기대하는 경우 유니언 타입을 사용할 수 있다.

type NullableString = string | null;
type StringOrNumber = string | number;

공통 필드를 가지는 유니언

두 타입이나 인터페이스에서 공통의 타입을 가지고 이를 유니언 타입으로 묶으면 공통된 타입은 접근이 가능하다.

interface Bird {
  fly(): void;
  layEggs(): void;
}

interface Fish {
  swim(): void;
  layEggs(): void;
}

declare function getSmallPet(): Fish | Bird;

let pet = getSmallPet();
pet.layEggs();
(pet as Fish).swim()

유니언 구별하기

리터럴을 갖는 단일 필드를 이용하여 유니언을 구별할 수 있다.

type NetworkLoadingState = {
    state: "loading"; // 리터럴
}

type NetworkFailedState = {
    state: "failed";
    code: number;
}

type NetworkState = NetworkLoadingState | NetworkFailedState

// NetworkState.state에 따라 유니언을 구별할 수 있다.

교차 타입

유니언과 다르게 두 타입의 값을 모두 포함해야 하는 경우 교차 타입을 통해 묶을 수 있다.

interface ErrorHandling {
  success: boolean;
  error?: { message: string };
}

interface ArtworksData {
  artworks: { title: string }[];
}

interface ArtistsData {
  artists: { name: string }[];
}

// 이 인터페이스들은
// 하나의 에러 핸들링과 자체 데이터로 구성됩니다.

type ArtworksResponse = ArtworksData & ErrorHandling;
type ArtistsResponse = ArtistsData & ErrorHandling;

인터페이스

인터페이스는 타입의 이름을 정하고 자신의 코드나 프로젝트 외부에서 사용하는 코드와 계약할 수 있게 도와준다.

? 를 사용하여 선택적 프로퍼티를 정의할 수 있고, readonly를 이용해 최초 할당 이후 오직 읽기만 가능한 타입을 만들 수 있다.

interface Point {
    x: number;
    y: number;
}

interface Device{
    readonly mac: string; // readonly
	name: string;
    type?: string; // optional
}

constreadonly

변수에 사용되는 경우 const, 프로퍼티로 사용되는 경우 readonly 를 사용한다.

함수 타입 인터페이스

인터페이스는 함수의 구조도 정의할 수 있다.

이 때 함수에서 받는 파라미터의 이름은 같지 않아도 된다.

interface AddFunc {
    (left: number, right: number): number;
}

let add: AddFunc;
add = function(left: number, right:number): number {
    return left + right
}

인덱서블 타입

특정 프로퍼티 이외에 여러 프로퍼티를 받으려면 [propName: type] 형태로 받아 처리할 수 있다.

인덱스 서명에는 numberstring 두 가지의 타입만 지원한다.

단, 숫자형은 실제로 문자형으로 변환되어 적용되므로, 문자형의 하위 호환이어야 한다.

ex) 100 과 “100”은 같은 값으로 처리됨

interface StringArray {
    length: number; // 배열의 길이를 나타내는 프로퍼티
    [index: number]: string; // 숫자 인덱스를 받아 문자열을 리턴하는 타입 
}

interface AnyType {
    [key: string]: any; // 아무 문자열을 키로 받아서 any라는 타입을 가진다.
}

// 단 문자열 시그니처를 사용시 반환 타입 (현재 any)이 모두 일치해야 한다.
interface SomeType {
    [key: string]: string;
    name: string;
    length: number; // 오류, 시그니처의 리턴타입이 string이므로, string만 가능하다.
}

인터페이스 확장

인터페이스에서는 extends를 이용해 확장이 가능하다.

이 때 ,를 이용해 여러 인터페이스를 확장하는 것도 가능하다.

interface Shape {
    color: string
}

interface Square extends Shape {
    sideLength: number
    // color: string 이 상속됨
}

interface PenStroke {
    penWidth: number
}

interface RoundPenStroke extends PenStroke, Shape {
    radius: number;
    // penWidth: number
    // color: string
}

하이브리드 타입

인터페이스는 다양한 JS의 타입을 지원하므로, 함수와 프로퍼티를 다 가지고 있는 타입도 사용 가능하다.

interface Counter {
    (start: number): string;
    interval: number;
    reset(): void
}


function getCounter(): Counter {
    let counter = (function (start: number) { }) as Counter;
    counter.interval = 123;
    counter.reset = function () { };
    return counter;
}

클래스를 확장한 인터페이스

인터페이스가 클래스의 타입을 확장하면 멤버는 상속되지만 구현은 상속되지 않는다.

class Control {
	private state: any;
}

interface SelectableControl extends Control {
    select(): void
}

class Button extends Control implements SelectableControl {
    select() { }
}

함수의 타이핑

함수의 타입은 아래와 같이 선언한다.

function add(x: number, y: number): number {
    return x + y;
}

const subtract: (x: number, y: number) => number = (x: number, y: number) => x - y

타입의 추론

함수 타입이나 리턴 값에 따라 타입을 추론한다.

function add(x: number, y: number) { // :number 가 없지만 리턴 타입이 number로 추론됨
    return x + y;
}


const subtract: (x: number, y: number) => 
	number = (x, y) => x - y 		// x, y의 타입이 없지만 타입에 따라 number로 추론됨

선택적, 기본 매개변수

인터페이스와 동일하게 파라미터에 ? 를 붙여 선택적 매개변수를 표현할 수 있다.

또한 = 를 이용해 기본 값을 지정하여 기본 매개변수를 표현할 수 있다.

단 선택적 매개변수에 한해서 항상 일반 매개변수보다 에 와야 한다.

function log(message: string, stamp?: number){ // (stamp?: number, message: string)은 허용되지 않음
    // stamp의 타입은 number | undefined
}

function log(message: string, stamp = new Date().getTime()){
    // stamp의 타입은 number
}

나머지 매개변수와 this

... 을 이용하여 나머지 매개변수를 받을 수 있고, this 를 이용하여 그 순간에 this 에 할당되는 타입을 지정할 수 있다.

function push(array: number[], ...items: number[]): void {
    // array 뒤에 오는 숫자 파라미터의 연속을 items 변수로 받는다.
}

interface Card{
    card: number;
}

interface Deck {
    numbers: number[]
    createCardPicker(this: Deck): () => Card;
}

const deck: Deck = {
    numbers: [1,2,3,4],
    createCardPicker: function(this: Deck){
        // 여기서의 this는 Deck 타입임
        const index = Math.floor(Math.random() * this.numbers.length)
        return () => {
            {
                card: this.numbers[index]
            }
        }
    }
}

오버로딩

동일한 함수를 여러번 선언하여 다중 함수 타입을 제공할 수 있다.

function pickCard(x: {suit: string; card: number; }[]): number;
function pickCard(x: number): {suit: string; card: number; };
function pickCard(x): any {
    // 인자가 배열 또는 객체인지 확인
    // 만약 그렇다면, deck이 주어지고 card를 선택합니다.
    if (typeof x == "object") {
        let pickedCard = Math.floor(Math.random() * x.length);
        return pickedCard;
    }
    // 그렇지 않다면 그냥 card를 선택합니다.
    else if (typeof x == "number") {
        let pickedSuit = Math.floor(x / 13);
        return { suit: suits[pickedSuit], card: x % 13 };
    }
}

리터럴 타입

변수 선언 시 let 으로 선언되면 변경 가능성을 알리지만 const 는 변경 되지 않음을 알린다.

const hello = "Hello" // "Hello" 자체로 타입
let world = "World" // "World"는 변할 수 있으므로 string 타입

type Easing = "ease-in" | "ease-out" | "ease-in-out"; // 문자형 리터럴
type Speed = 0.5 | 1 | 1.5 | 2; // 숫자형 리터럴

제네릭

제네릭을 이용하면 타입에 사용될 타입을 <> 를 통해 받을 수 있다.

인터페이스나 클래스, 함수에서도 사용할 수 있다.

, 를 통해 여러 타입을 받을 수 있다.

type Nullable<T> = T | null // Nullable<string>은 string과 null의 유니온 타입

interface SomeInterface<T>{
    value: T
}

function someFunc<T>(arg: T): T {
    return T
}

class SomeClass<T, K> {
    fieldT: T
    fieldK: K 
}

Written by@[esllo]
plain developer

GitHubTwitterLinkedIn